package org.prime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.SortedSet;
import java.util.TreeSet;


//@author Mateusz Gadziala
//@see Management
public class Management implements Runnable {                                   


        private ArrayList<SubProcess> threads; // available threads collection
        private Iterator<SubProcess> iteratorForThreads;
        private SortedSet<LongInteger> primeNumbers; // calculated prime numbers
        private Iterator<LongInteger> iteratorForPrimeNumbers;


        // ************** conditions values
        private LongInteger numberOfPrimeNumbers; // number of prime numbers after
                                                                                                // stop - if 0 - no checking
                                                                                                // this parameter before stop
        private LongInteger maxPrimeNumber; // value of the biggest prime numbers
                                                                                // after stop - if 0 - no checking this
                                                                                // parameter before stop
        private long maxTime; // max time for calculation
        private int stabilizationRange; // number of gathered threadIntervalTime for
                                                                        // thread


        // size of LongInteger
        public static int primeNumberSize = 512;
        private int numberOfThreads; // ordered number of threads - if 0 - system
                                                                        // managing


        // ************* managing values
        private LongInteger typicalBuffer; // prime numbers package size or prime
                                                                                // numbers to calculate - on firsts
                                                                                // round
        private long typicalComputeTime; // max computation time for thread
        private long typicalStreamTime;
        private LongInteger currentNumberOfPrimeNumbers;// max streaming time
        private LongInteger currentMaxPrimeNumber;
        private int numberOfActiveThreads;


        public Management(int primeNumbersize, int numberOfThreads)
                        throws Exception {
                // start first thread,
                // get package, run it again, send package to next
                // start next thread,
                LongInteger liczba0 = new LongInteger ();
                currentNumberOfPrimeNumbers = liczba0;
                this.numberOfThreads = numberOfThreads;
                numberOfActiveThreads = 0;
                System.out.println("requested number of threads: " + numberOfThreads);
                if (!this.init())
                        throw new Exception(
                                        "Error in threads initialization in Management()");
                Management.primeNumberSize = primeNumbersize;
        }


        private void manageThreads() { // main function managing threads operations


                action: while (true) { // always searching for ready thread
                        iteratorForThreads = threads.iterator();
                        while (this.iteratorForThreads.hasNext()) {
                                
                                SubProcess tempThread = iteratorForThreads.next();


//                             System.out.println("checking thread no. "+tempThread.id);
                        
                                        tempThread.intTime = 0;


                                
                                                if (tempThread.primeNumberSize == Integer.parseInt(tempThread.bufferSize.toString()))
                                                {
                                                        synchronized(this){
                                                                SortedSet<LongInteger> tempPackage = tempThread.getPrimePackageReturn();
                                                                this.primeNumbers.addAll(tempPackage);
                                                                tempThread.setPrimePackage(this.primeNumbers);
                                                                tempThread.beginningValue = this.primeNumbers.last().add2(typicalBuffer);
                                                                
                                                                tempThread.run();
                                                        }
                                                }
                                        this.currentNumberOfPrimeNumbers.add(tempThread.primePackageReturnSize);


                                //      System.out.println("thread id: "+ tempThread.id + "begging value: "+tempThread.beginningValue.toString() + "buffer size: "+tempThread.bufferSize.toString());
                        }


                        this.currentNumberOfPrimeNumbers.set(primeNumbers.size());
                        this.currentMaxPrimeNumber = this.primeNumbers.last();
                       // if (currentMaxPrimeNumber.equals(new LongInteger("0")))
                                if(currentNumberOfPrimeNumbers.compareTo(this.numberOfPrimeNumbers) >= 0)
                                        break;
                       // else if (currentMaxPrimeNumber.compareTo(this.maxPrimeNumber) >= 0)
                          //      break;
                
                }
               // this.print();


        }


        public void calculatePrimeNumbers(LongInteger size) { // calculating size prime
                                
                LongInteger liczba29=new LongInteger ();
                liczba29.set(29);
                
                // numbers
                this.numberOfPrimeNumbers = size;
                for (int i = 0; i < this.numberOfThreads; i++) { // creating
                                                                                                                        // numberOfThreads
                        System.out.println("i: " + i);
                        if (i == 0) // if it is first thread, start calculation with example
                                                // package;
                        {
                                System.out.println("ThreadAdded: 0");
                                
                                threads.get(0).setPrimePackage(primeNumbers);
                                threads.get(0).bufferSize = this.typicalBuffer;
                                
                                threads.get(0).beginningValue = liczba29;
                                threads.get(0).start();
                        } else {
                                System.out.println("ThreadAdded: x");
                                threads.get(i).setPrimePackage(primeNumbers);
                                threads.get(i).bufferSize = this.typicalBuffer;
                                threads.get(1).beginningValue=(this.primeNumbers.last().multiply2(this.primeNumbers.last())) ;
                                threads.get(i).start();
                        }
                }
                this.run();
        }

        public void calculateMaxPrimeNumber(LongInteger max) {
                LongInteger zero = new LongInteger();
            zero.set(0);
                this.maxPrimeNumber = max;
                this.calculatePrimeNumbers(zero);
                
                return;
        }


        public void factorizeNumber(LongInteger number) {
                System.out.println("Method not avaible");
                
                if (this.maxPrimeNumber.lesser(number.sqrt()) ){
                        this.calculateMaxPrimeNumber(number.sqrt());
                        factorizeNumber(number);
                }
        
                LinkedList<LongInteger> factors = new LinkedList<LongInteger>();
                
                LongInteger jeden = new LongInteger();
                jeden.set(1);
                LongInteger zero = new LongInteger();
                zero.set(0);
                while (this.iteratorForPrimeNumbers.hasNext()){
                        
                        LongInteger n = this.iteratorForPrimeNumbers.next();
                        if (number.divisibleBy(n))
                        {
                                factors.addLast(n);
                                number = (LongInteger) number.divide(n);
                        }
                        if (number.equals(jeden) || number.equals(zero))
                                break;
                                        
                }
                
                Iterator<LongInteger> fac = factors.iterator();
                System.out.println("Factorized number: "+number.toString());
                while (fac.hasNext())
                {
                        System.out.println(fac.next().toString());
                }
                
                return;
        }


        public void run() {


                this.manageThreads();
                // TODO print everything
        }


        public void print() {
                System.out.println(this.primeNumbers);
        }


        private void addPrimeNumbers(int threadId) {


        }


        private LongInteger changeBufferSize(LongInteger newBufferSize) {
                LongInteger zero = new LongInteger();
                zero.set(0);
                if (newBufferSize.compareTo(numberOfPrimeNumbers) >=0) {
                return newBufferSize;
                }
                return (zero) ;
        }


        private boolean init() {
                        this.typicalBuffer=new LongInteger ();
                this.typicalBuffer.set(10000);
                this.stabilizationRange = 5;
                primeNumbers = new TreeSet<LongInteger>();
                LongInteger e = new LongInteger ();
                e.set(2);
                primeNumbers.add(e);
                e.set(3);
                primeNumbers.add(e);
                
                e.set(5);
                primeNumbers.add(e);
                e.set(7);
                primeNumbers.add(e);
                e.set(11);
                primeNumbers.add(e);
                e.set(13);
                primeNumbers.add(e);
                e.set(17);
                primeNumbers.add(e);
                e.set(19);
                primeNumbers.add(e);
                e.set(23);
                primeNumbers.add(e);
                e.set(29);
                primeNumbers.add(e);
                e.set(31);
                primeNumbers.add(e);
                this.print();
                threads = new ArrayList<SubProcess>();


                for (int i = 0; i < this.numberOfThreads; i++) {
                        SubProcess SP = new SubProcess();
                        threads.add(SP);
                        this.numberOfActiveThreads++;


                }
                return true;
        }


}